package concurrecy.lock;

import sun.reflect.generics.reflectiveObjects.NotImplementedException;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

class FairLockChecker implements Runnable {

    private Lock lock;
    private int count = 0;

    public FairLockChecker(Lock lock) {
        this.lock = lock;
    }

    @Override
    public String toString() {
        return Thread.currentThread().getName();
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                lock.lockInterruptibly();
                try {
                    count ++;
                    TimeUnit.MILLISECONDS.sleep(100);
                } finally {
                    lock.unlock();
                }
            }
            System.out.println(this + " exit");
        } catch(InterruptedException e) {
        } finally {
            System.out.println(this + " : " + count);
        }
    }
}

class MyFairLock extends MyLock {
    @Override
    public void lock() {
        throw new NotImplementedException();
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        if(!waitQueue.isEmpty() || !tryLock()) {
            Thread t = Thread.currentThread();
            // 没有抢到锁
            waitQueue.add(t);
            for(;;) {
                if(Thread.interrupted())
                    throw new InterruptedException("lock interrupted");
                if(waitQueue.peek() == t && tryLock()) {
                    waitQueue.poll();
                    return;
                }
                LockSupport.park(); // 进入等待状态
            }
        }
    }
}

/**
 * 公平锁
 */
public class MyLockDemo1 {
    public static void main(String[] args) throws InterruptedException {
        Lock lock = new MyFairLock();
//        Lock lock = new ReentrantLock(true);
        ExecutorService service = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            service.execute(new FairLockChecker(lock));
        }
        TimeUnit.SECONDS.sleep(10);
        service.shutdownNow();
    }
}
